package com.zp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import com.zp.constants.UserConstant;
import com.zp.domain.entity.User;
import com.zp.domain.request.user.UserQueryRequest;
import com.zp.domain.vo.CaptchaVO;
import com.zp.domain.vo.LoginUserVO;
import com.zp.domain.vo.UserVO;
import com.zp.enums.ErrorCode;
import com.zp.enums.UserRoleEnum;
import com.zp.exception.BusinessException;
import com.zp.manager.auth.StpKit;
import com.zp.manager.message.EmailManager;
import com.zp.mapper.UserMapper;
import com.zp.service.UserService;
import com.zp.utils.ThrowUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private EmailManager emailManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 用户注册
     *
     * @param userEmail 用户邮箱
     * @param codeKey   验证码 key
     * @param codeValue 验证码 value
     */
    @Override
    public long userRegister(String userEmail, String codeKey, String codeValue) {
        // 1. 获取验证码
        final String redisKey = "email_code_key" + codeKey + userEmail;
        String code = stringRedisTemplate.opsForValue().get(redisKey);
        if (StrUtil.isBlank(code) || !code.equals(codeValue)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
        }
        // 2. 判断账号是否存在
        if (this.queryUserByEmail(userEmail)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在, 请直接登录!");
        }
        // 3. 创建用户
        User user = new User();
        String encryptPassword = this.encryptPassword("12345678");
        user.setUserPassword(encryptPassword);
        user.setUserEmail(userEmail);
        user.setUserAccount("user_" + RandomUtil.randomInt(9999));
        user.setUserName("用户_" + RandomUtil.randomInt(9999));
        user.setUserAvatar("https://img.shetu66.com/2023/04/25/1682391068745168.png");
        user.setUserProfile("这个人很懒，什么都没有留下~");
        user.setUserRole(UserRoleEnum.USER.getValue());
        boolean saved = this.save(user);
        if (saved) {
            stringRedisTemplate.delete(redisKey);
            // 发送初始密码
            emailManager.sendEmailAsRegisterSuccess(userEmail, "拾光匣-注册成功通知");
        }
        // 8. 返回用户 id
        return user.getId();
    }

    /**
     * 用户登录
     *
     * @param userAccount  用户账号
     * @param userPassword 用户密码
     * @param captchaKey   图形验证码 key
     * @param captchaCode  图形验证码 验证码
     * @param request      存储登录信息到 session
     * @return 登录用户信息(脱敏)
     */
    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, String captchaKey, String captchaCode, HttpServletRequest request) {
        // 1. 获取验证码
        final String redisKey = "captcha_code_key" + captchaKey;
        String code = stringRedisTemplate.opsForValue().get(redisKey);
        // 删除验证码
        stringRedisTemplate.delete(redisKey);
        ThrowUtils.throwIf(StrUtil.isBlank(code) || !code.equals(captchaCode), ErrorCode.PARAMS_ERROR, "验证码错误");
        User user;
        if (ReUtil.isMatch(RegexPool.EMAIL, userAccount)) {
            // 根据邮箱查询用户
            user = this.getUserByUserEmail(userAccount);
        } else {
            // 根据用户账号查询用户
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_account", userAccount);
            user = this.baseMapper.selectOne(queryWrapper);
        }
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR, "用户不存在或密码错误");
        // 2. 密码加密
        String encryptPassword = this.encryptPassword(userPassword);
        if (!user.getUserPassword().equals(encryptPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        // 4. 保存用户的登录态到 session
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
        // 记录用户登录态到 Sa-token, 便于空间鉴权时使用, 注意保证该用户信息与 SpringSession 中的信息过期时间一致
        StpKit.SPACE.login(user.getId());
        StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, user);
        // 5. 返回脱敏后的用户信息(封装一个方法)
        return this.getLoginUserVO(user);
    }

    /**
     * 获取当前登录用户
     *
     * @param request 用户的登录态
     * @return 当前登录用户
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 1. 判断用户是否登录
        User currentUser = (User) request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        ThrowUtils.throwIf(ObjUtil.isNull(currentUser), ErrorCode.NOT_LOGIN_ERROR, "请先登录");
        // 2. 从数据库中查询出当前的登录用户
        long userId = currentUser.getId();
        ThrowUtils.throwIf(userId <= 0, ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        currentUser = this.getById(userId);
        ThrowUtils.throwIf(ObjUtil.isNull(currentUser), ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        // 3. 返回用户信息
        return currentUser;
    }

    /**
     * 密码加密
     *
     * @param userPassword 密码
     * @return 加密后的密码
     */
    @Override
    public String encryptPassword(String userPassword) {
        ThrowUtils.throwIf(StrUtil.isBlank(userPassword), ErrorCode.PARAMS_ERROR);
        // 密码加密
        return DigestUtils.md5DigestAsHex((UserConstant.SALT + userPassword).getBytes());
    }

    /**
     * 获取用户信息(脱敏)
     *
     * @param user 用户信息
     * @return 脱敏后的用户信息
     */
    @Override
    public LoginUserVO getLoginUserVO(User user) {
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtil.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    /**
     * 用户注销
     *
     * @param request 用户登录态
     * @return 是否注销成功
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        // 1. 判断用户是否登录
        User currentUser = (User) request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        ThrowUtils.throwIf(ObjUtil.isNull(currentUser), ErrorCode.NOT_LOGIN_ERROR, "请先登录");
        // 2. 已经登录, 移除登录态
        request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
        return true;
    }

    /**
     * 获取用户信息(脱敏)
     *
     * @param user 用户信息
     * @return 脱敏后的用户信息
     */
    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtil.copyProperties(user, userVO);
        return userVO;
    }

    /**
     * 获取用户列表(脱敏)
     *
     * @param userList 用户列表
     * @return 脱敏后的用户列表
     */
    @Override
    public List<UserVO> getUserVOList(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return Collections.emptyList();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    /**
     * 分页查询条件封装
     *
     * @param queryRequest 查询参数
     * @return 查询条件
     */
    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest queryRequest) {
        if (queryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        Long id = queryRequest.getId(); // eq
        String userName = queryRequest.getUserName(); // like
        String userAccount = queryRequest.getUserAccount(); // eq
        String userProfile = queryRequest.getUserProfile(); // like
        String userRole = queryRequest.getUserRole(); // eq
        String sortField = queryRequest.getSortField(); // 排序字段
        String sortOrder = queryRequest.getSortOrder(); // 排序顺序
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);
        queryWrapper.eq(StrUtil.isNotBlank(userAccount), "user_account", userAccount);
        queryWrapper.eq(StrUtil.isNotBlank(userRole), "user_role", userRole);
        queryWrapper.like(StrUtil.isNotBlank(userName), "user_name", userName);
        queryWrapper.like(StrUtil.isNotBlank(userProfile), "user_profile", userProfile);
        queryWrapper.orderBy(StrUtil.isNotBlank(sortField), sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }

    /**
     * 判断是否是管理员
     *
     * @param user 用户
     * @return 判断是否是管理员
     */
    @Override
    public boolean isAdmin(User user) {
        if (ObjUtil.isEmpty(user)) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 根据用户邮箱判断用户是否存在
     *
     * @param userEmail 用户邮箱
     * @return 用户是否存在
     */
    @Override
    public boolean queryUserByEmail(String userEmail) {
        ThrowUtils.throwIf(StrUtil.isBlank(userEmail), ErrorCode.PARAMS_ERROR);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_email", userEmail);
        long count = this.count(queryWrapper);
        return count > 0;
    }

    /**
     * 根据用户邮箱查询用户
     *
     * @param userEmail 用户邮箱
     * @return 用户信息
     */
    @Override
    public User getUserByUserEmail(String userEmail) {
        ThrowUtils.throwIf(StrUtil.isBlank(userEmail), ErrorCode.PARAMS_ERROR);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_email", userEmail);
        return this.getOne(queryWrapper);
    }

    /**
     * 发送邮箱验证码
     *
     * @param userEmail 用户邮箱
     * @return 验证码 key
     */
    @Override
    public String sendEmailCode(String userEmail) {
        boolean updated = this.queryUserByEmail(userEmail);
        if (updated) {
            // 邮箱已存在
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "账号已存在，请直接登录！");
        }
        // 发送邮箱验证码
        String code = RandomUtil.randomNumbers(6);
        emailManager.sendEmailCode(userEmail, "拾光匣 - 注册验证码", code);
        // 生成一个唯一 ID, 后面注册前端需要带过来
        String key = UUID.randomUUID().toString(true);
        // 缓存验证码 5 分钟
        final String redisKey = "email_code_key" + key + userEmail;
        stringRedisTemplate.opsForValue().set(redisKey, code, 5, TimeUnit.MINUTES);
        return key;
    }

    /**
     * 获取图形验证码
     *
     * @return 图形验证码
     */
    @Override
    public CaptchaVO getCaptcha() {
        // 指定宽度高度以及验证码的个数
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
        // 验证码类型: 仅数字
        specCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER);
        // 获取验证码字符串 "2574", 对于数字转小写没必要, 这里是为了更通用
        String captchaCode = specCaptcha.text().toLowerCase();
        // 获取验证码图片, 转成 base64, 前端可以写在 img 标签的 src 属性中, 简化开发
        String captchaImage = specCaptcha.toBase64();
        String captchaKey = UUID.randomUUID().toString(true);
        // 把验证码存入 Redis 并且设置 1 分钟过期
        final String redisKey = "captcha_code_key" + captchaKey;
        stringRedisTemplate.opsForValue().set(redisKey, captchaCode, 1, TimeUnit.MINUTES);
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaKey(captchaKey);
        captchaVO.setCaptchaImage(captchaImage);
        return captchaVO;
    }
}




